/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */

package org.solmix.fmk.config.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.solmix.commons.collections.DataTypeMap;
import org.solmix.commons.logs.Logger;
import org.solmix.commons.util.DataUtil;
import org.solmix.fmk.config.ConfigRealm;
import org.solmix.fmk.config.ConfigurationEntry;

/**
 * @org.apache.xbean.XBean element="config"
 */
public class ConfigRealmImpl implements ConfigRealm
{

    private static final Logger log = new Logger(ConfigRealmImpl.class.getName());

    public static final String PROPERTY_MODULE = "org.solmix.module";

    public static final String PROPERTY_BUNDLE = "org.solmix.bundle.ID";

    private String name;

    private int rank;

    private List<Module> modules;

    private BundleContext bundleContext;

    private List<ConfigurationEntry> entries;

    /**
     * cache configuration for this service.
     */
    protected  ConfigCache localcache = new ConfigCache();

    protected boolean cachingEnabled = true;

    /**
     * @return the rank
     */
    public int getRank() {
        return rank;
    }

    /**
     * @param rank the rank to set
     */
    public void setRank(int rank) {
        this.rank = rank;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the modules
     */
    public List<Module> getModules() {
        return modules;
    }

    /**
     * @param modules the modules to set
     */
    public void setModules(List<Module> modules) {
        this.modules = modules;
        this.entries = null;
    }

    /**
     * @return the bundleContext
     */
    public BundleContext getBundleContext() {
        return bundleContext;
    }

    /**
     * @param bundleContext the bundleContext to set
     */
    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }

    private ConfigurationEntry.ModuleControlFlag getControlFlag(String flags) {
        if ("required".equalsIgnoreCase(flags)) {
            return ConfigurationEntry.ModuleControlFlag.REQUIRED;
        }
        if ("optional".equalsIgnoreCase(flags)) {
            return ConfigurationEntry.ModuleControlFlag.OPTIONAL;
        }
        if ("requisite".equalsIgnoreCase(flags)) {
            return ConfigurationEntry.ModuleControlFlag.REQUISITE;
        }
        if ("sufficient".equalsIgnoreCase(flags)) {
            return ConfigurationEntry.ModuleControlFlag.SUFFICIENT;
        }
        return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#getEntries()
     */
    @Override
    public List<ConfigurationEntry> getEntries() {
        if (this.entries == null && this.modules != null) {
            List<ConfigurationEntry> res = new ArrayList<ConfigurationEntry>();
            List<Module> modules = this.modules;
            for (Module module : modules) {
                Map<String, Object> options = new HashMap<String, Object>();
                options.put(BundleContext.class.getName(), bundleContext);
                if (module.getCm() != null) {
                    for (Map.Entry e : module.getCm().getOptions().entrySet()) {
                        options.put(e.getKey().toString(), e.getValue());
                    }
                }
                options.put(PROPERTY_MODULE, module.getName());
                options.put(PROPERTY_BUNDLE, Long.toString(bundleContext.getBundle().getBundleId()));
                ConfigurationEntry.ModuleControlFlag flags = getControlFlag(module.getFlags());
                if (flags == null)
                    flags = ConfigurationEntry.ModuleControlFlag.REQUIRED;
                res.add(new ConfigurationEntry(module.getName(), flags, options));
            }
            this.entries = res;
        }
        return this.entries;
    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:prodel,config:update.
     * 
     * @param properties
     * @throws Exception
     */
    public void deleteProperties(List properties) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:prodel,config:update.
     */
    public void deleteProperty(String key) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:prodel,config:update.
     */
    public void deleteProperties(List properties, String propertyFile) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:prodel,config:update.
     */
    public void deleteProperty(String key, String propertyFile) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:update.
     */
    public void persistProperties(Map properties, String propertyFile) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:update.
     */
    public void persistProperties(Map properties) throws Exception {

    }

    public void persistProperty(String key, String value) throws Exception {

    }

    /**
     * do nothing .the function is provided by OSGI Configuration management service.
     * <p>
     * for karaf OSGI runtime ,the command is :config:edit,config:update.
     */
    public void persistProperty(String key, String value, String propertyFile) throws Exception {

    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#asProperties()
     */
    @Override
    public Properties asProperties() {
        return buildProperties(this.getLocalConfig(null));
    }

    public Properties buildProperties(Map map) {
        return (Properties) DataUtil.mapMerge(map, new Properties());
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#clearCache()
     */
    @Override
    public void clearCache() {
        if (localcache != null && cachingEnabled)
            localcache.clearCache();

    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#getConfigBlock(java.lang.String)
     */
    @Override
    public DataTypeMap getConfigBlock(String key) {
        return new DataTypeMap(DataUtil.getPrefixed(key, getLocalCache()));
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#getModuleConfig(java.lang.String)
     */
    @Override
    public DataTypeMap getModuleConfig(String key) {
        return new DataTypeMap(getLocalConfig(key));
    }

    /**
     * wrapped List {@link org.solmix.fmk.config.ConfigurationEntry ConfigurationEntry} into
     * {@link org.solmix.commons.collections.DataTypeMap DataTypeMap}
     * 
     * @param moduleName
     * @return
     */
    @Override
    public Map<String, Object> getLocalConfig(String moduleName) {
        Map<String, Object> osgi=null;
        try {
            osgi= getOSGIManagedConfig(moduleName);
        } catch (IOException e) {
            log.warn("",e);
        }
        Map<String, Object> options = null;
        if (this.modules != null) {
            List<Module> modules = this.modules;
            for (Module module : modules) {
                if (DataUtil.isNullOrEmpty(moduleName)||(moduleName.equals(module.getName()))) {
                        if (module.getCm() != null) {
                            for (Map.Entry e : module.getCm().getOptions().entrySet()) {
                                if(options==null)
                                    options= new HashMap<String, Object>();
                                options.put(e.getKey().toString(), e.getValue());
                            }
                    }
                }
            }
        }
        if(options==null)
            return osgi;
        Map<String, Object> merged =DataUtil.mapMerge(osgi,  options);
        return merged;
    }

    protected Map<String, Object> getOSGIManagedConfig(String moduleName) throws IOException {

        ServiceReference cfgAdmin = this.bundleContext.getServiceReference(ConfigurationAdmin.class.getName());
        ConfigurationAdmin service = null;
        if (cfgAdmin != null) {
            service = (ConfigurationAdmin) this.bundleContext.getService(cfgAdmin);
        } else {
            log.warn("can't get service:[" + ConfigurationAdmin.class.getName() + "]");
        }
        if (service == null)
            return null;
        Map<String, Object> options = new HashMap<String, Object>();
        if (this.modules != null) {
            List<Module> modules = this.modules;
            for (Module module : modules) {
                if (DataUtil.isNullOrEmpty(moduleName)||(moduleName.equals(module.getName()))) {
                    String pid = module.getPersistentId();
                    Configuration conf = service.getConfiguration(pid);
                    Dictionary props = conf.getProperties();
                    if (props != null) {
                        for (Enumeration e = props.keys(); e.hasMoreElements();) {
                            Object key = e.nextElement();
                            options.put(key.toString(), props.get(key));
                        }
                    }
                }
            }
        }
        return options;

    }

    /**
     * @return local global cache.
     */
    public Map<String, Object> getLocalCache() {
        if (cachingEnabled) {
            if (localcache.isNull())
                localcache.putAll(getLocalConfig(null));
            return localcache.get();
        } else {
            return getLocalConfig(null);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#getString(java.lang.String, java.lang.String)
     */
    @Override
    public String getString(String key, String defaultValue) {
        Object s = getLocalCache().get(key);
        if (s == null)
            return defaultValue;
        else
            return s.toString().trim();
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#getSubtree(java.lang.String)
     */
    @Override
    public DataTypeMap getSubtree(String key) {
        return new DataTypeMap(DataUtil.getSubtreePrefixed(key, getLocalCache()));
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public boolean put(String key, Object value) {
        if (cachingEnabled) {
            localcache.put(key, value);
            return true;
        } else {
            throw new RuntimeException(
                "cachingEnabled for config cache is false ,can't modified configuration,managment function provied by osgi container");
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#putAll(java.util.Map)
     */
    @Override
    public void putAll(Map t) {
        if (cachingEnabled) {
            localcache.putAll(t);
        } else {
            throw new RuntimeException(
                "cachingEnabled for config cache is false ,can't modified configuration,managment function provied by osgi container");
        }

    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.fmk.config.ConfigRealm#remove(java.lang.Object)
     */
    @Override
    public boolean remove(String key) {
        if (cachingEnabled) {
            localcache.remove(key);
            return true;
        } else {
            throw new RuntimeException(
                "cachingEnabled for config cache is false ,can't modified configuration,managment function provied by osgi container");
        }

    }
}
